package priv.pfz.basedao.query;

import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import priv.pfz.basedao.Utils;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;

/**
 * @author yyjxp
 * 2024/1/28 20:26
 */
@AllArgsConstructor
public class BaseColumn<Column, Query extends BaseQuery<Query>> {
    protected final Query query;
    protected final String fieldName;
    protected final String columnName;
    protected final String columnSql;

    public BaseColumn(Query query, String fieldName, String columnName) {
        this.query = query;
        this.fieldName = fieldName;
        this.columnName = columnName;
        this.columnSql = "`" + columnName + "`";;
    }

    protected boolean isValueEmpty(Column value) {
        return value == null;
    }

    /**
     * xxx = 'abc'
     */
    public Query eq(@Nullable Column value) {
        return commonKvOp(value, OpType.EQ);
    }

    /**
     * xxx != 'abc'
     */
    public Query ne(@Nullable Column value) {
        return commonKvOp(value, OpType.NE);
    }

    /**
     * xxx > 'abc'
     */
    public Query gt(@Nullable Column value) {
        return commonKvOp(value, OpType.GT);
    }

    /**
     * xxx < 'abc'
     */
    public Query lt(@Nullable Column value) {
        return commonKvOp(value, OpType.LT);
    }

    /**
     * xxx >= 'abc'
     */
    public Query gte(@Nullable Column value) {
        return commonKvOp(value, OpType.GTE);
    }

    /**
     * xxx <= 'abc'
     */
    public Query lte(@Nullable Column value) {
        return commonKvOp(value, OpType.LTE);
    }

    protected Query commonKvOp(Column value, OpType opType) {
        if (isValueEmpty(value)) {
            return query;
        }
        String placeholder = query.addValue(value);
        Condition<Column> condition = new Condition<>();
        condition.setFieldName(fieldName);
        condition.setOpType(opType);
        condition.addValue(value);
        condition.setSqlFormat(columnSql + " " + opType.getOp() + " " + placeholder);
        query.addCondition(condition);
        return query;
    }

    /**
     * xxx IS NULL
     */
    public Query isNull() {
        commonKeyOp(OpType.IS_NULL);
        return query;
    }

    /**
     * xxx IS NOT NULL
     */
    public Query notNull() {
        commonKeyOp(OpType.NOT_NULL);
        return query;
    }

    protected Query commonKeyOp(OpType opType) {
        Condition<Column> condition = new Condition<>();
        condition.setFieldName(fieldName);
        condition.setOpType(opType);
        condition.setSqlFormat(columnSql + " " + opType.getOp());
        query.addCondition(condition);
        return query;
    }

    /**
     * xxx IN ('a', 'b', 'c')
     */
    public Query in(@Nullable Iterable<Column> values) {
        if (values == null || !values.iterator().hasNext()) {
            return query;
        }
        return commonInOp(Lists.newArrayList(values), OpType.IN);
    }

    /**
     * xxx IN ('a', 'b', 'c')
     */
    @SafeVarargs
    public final Query in(@Nullable Column... values) {
        if (values == null || values.length == 0) {
            return query;
        }
        return commonInOp(Lists.newArrayList(values), OpType.IN);
    }

    /**
     * xxx NOT IN ('a', 'b', 'c')
     */
    public Query notIn(@Nullable Iterable<Column> values) {
        if (values == null || !values.iterator().hasNext()) {
            return query;
        }
        return commonInOp(Lists.newArrayList(values), OpType.NOT_IN);
    }

    /**
     * xxx NOT IN ('a', 'b', 'c')
     */
    @SafeVarargs
    public final Query notIn(@Nullable Column... values) {
        if (values == null || values.length == 0) {
            return query;
        }
        return commonInOp(Lists.newArrayList(values), OpType.NOT_IN);
    }

    private Query commonInOp(List<Column> values, OpType opType) {
        Condition<Column> condition = new Condition<>();
        condition.setFieldName(fieldName);
        condition.setOpType(opType);
        StringBuilder sb = new StringBuilder(columnSql).append(" ").append(opType.getOp()).append(" (");
        for (Column value : values) {
            String placeholder = query.addValue(value);
            condition.addValue(value);
            sb.append(placeholder).append(", ");
        }
        condition.setSqlFormat(sb.delete(sb.length() - 2, sb.length()).append(")").toString());
        query.addCondition(condition);
        return query;
    }

    /**
     * xxx BETWEEN 'a' AND 'b'
     */
    public Query between(@Nonnull Column from, @Nonnull Column to) {
        return commonBetweenOp(from, to, OpType.BETWEEN);
    }

    /**
     * xxx NOT BETWEEN 'a' AND 'b'
     */
    public Query notBetween(@Nonnull Column from, @Nonnull Column to) {
        return commonBetweenOp(from, to, OpType.NOT_BETWEEN);
    }

    private Query commonBetweenOp(Column from, Column to, OpType opType) {
        Utils.checkArgument(!isValueEmpty(from), "between参数不能为空");
        Utils.checkArgument(!isValueEmpty(to), "between参数不能为空");
        String placeholderFrom = query.addValue(from);
        String placeholderTo = query.addValue(to);
        Condition<Column> condition = new Condition<>();
        condition.setFieldName(fieldName);
        condition.setOpType(opType);
        condition.addValue(from);
        condition.addValue(to);
        condition.setSqlFormat(columnSql + " " + opType.getOp() + " " + placeholderFrom + " AND " + placeholderTo);
        query.addCondition(condition);
        return query;
    }

    /**
     * ORDER BY xxx ASC
     */
    public Query asc() {
        query.orderBys.add(columnSql + " ASC");
        return query;
    }

    /**
     * ORDER BY xxx DESC
     */
    public Query desc() {
        query.orderBys.add(columnSql + " DESC");
        return query;
    }
}
